重新开坑
<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=330 height=86 src="//music.163.com/outchain/player?type=3&id=907727144&auto=1&height=66"></iframe>
奉上最近觉得的神作(至少从小说与这首曲子来说是这样的)。
之前写到过可持久化并查集三部曲,现在想来,唯独没有提到按秩合并,在研习了启发式合并后,决定重新为并查集写一份外传,记录并查集的另一作用。
什么是按秩合并,就小编来看,其实就是启发式合并的一种,在满足不路径压缩改变fa[x]的前提下,能够使得并查集的深度为$log^n$级别的。
但是提醒,这里的按秩合并与路径压缩并不矛盾,按秩合并关键在union函数,而路径压缩是在find函数中,若只是普通并查集,就只需要上路径压缩就可以了,应该没有哪个丧尽天良的出题人为了卡路径压缩要求两个优化都用才能AC。。。。
如何实现,先上代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int fa[N],f[N]; int siz[N],dpn[N]; int cnt_merge = 0; int find(int x){ if(fa[x]==x)return x; int k=findfather(fa[x]); dpn[x]=dpn[fa[x]]+1; return k; } inline void merge(int x,int y){ x=find(x),y=find(y); if(x==y)return void(++cnt_merge); if(siz[x]>siz[y])fa[y]=x,f[y]=++cnt_merge,siz[x]+= siz[y]; else fth[x]=y,f[x]=++cnt_merge,siz[y]+=siz[x]; }
|
这样写有什么用呢,或者说为什么这么写?
在两个不同子集连接时,如果我们按照rank来连接,把rank低的连在rank高的下面,让rank高的做father,那么我们就相当于维护了一棵树,保证了类似于启发式合并一样的树高最坏$log^n$。
神奇吧,来一道题清爽一下。
BZOJ4668传送门
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include<cstdio> const int N=5e5+5; int f[N],d[N],r[N],p[N]; int find(int i){ if(p[i]==i)return i; register int j=find(p[i]); d[i]=d[p[i]]+1; return j; } int unionn(int i,int j){ i=find(i),j=find(j); if(i==j)return 0; if(r[i]==r[j])++r[j]; if(r[i]<r[j]){ p[i]=j; return i; } p[j]=i; return j; } int query(int i,int j){ register int s=0; if(find(i)==find(j)) while(i!=j) if(d[i]>d[j])s<f[i]?s=f[i]:0,i=p[i]; else s<f[j]?s=f[j]:0,j=p[j]; return s; } inline void read(int &res){ static char ch; while((ch=getchar())<'0'||ch>'9');res=ch-48; while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48; } int main(){ register int n,m,k,s,t,last=0; read(n),read(m); for(;n;--n)p[n]=n; while(m--){ read(k),read(s),read(t); s^=last,t^=last; if(k)printf("%d\n",last=query(s,t)); else f[unionn(s,t)]=++n; } return 0; }
|
哈哈我可没有忘记并查集系列的看板娘